package com.xh.demo.interceptor;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.xh.demo.commons.annotation.NotRepeatSubmit;
import com.xh.demo.commons.consts.RConst;
import com.xh.demo.commons.entity.TokenInfo;
import com.xh.demo.commons.utils.SignUtil;
import com.xh.demo.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

@Component
public class TokenInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private RedisService redisService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader(RConst.Security.TOKEN_KEY);
        String timestamp = request.getHeader(RConst.Security.TIMESTAMP);
        String nonce = request.getHeader(RConst.Security.NONCE);
        String sign = request.getHeader(RConst.Security.SIGN);
        Assert.isFalse(StrUtil.isEmpty(token) || StrUtil.isEmpty(timestamp) || StrUtil.isEmpty(sign), "参数错误");
        // 获取超时时间
        NotRepeatSubmit notRepeatSubmit = this.getNotRepeatSubmit(handler);
        long expireTime = notRepeatSubmit == null ? RConst.Time.TIME_5_MINUTE : notRepeatSubmit.value();
        // 2. 请求时间间隔
        long requestInterval = System.currentTimeMillis() - Long.parseLong(timestamp);
        Assert.isTrue(requestInterval < expireTime, "请求超时，请重新请求");
        // 3. 校验Token是否存在
        TokenInfo tokenInfo = redisService.getJSON2Obj(token, TokenInfo.class);
        Assert.notNull(tokenInfo, "token错误");
        // 4. 校验签名(将所有的参数加进来，防止别人篡改参数) 所有参数看参数名升续排序拼接成url
        // 请求参数 + token + timestamp + nonce
        String signature = SignUtil.sign(request, (tokenInfo.getAppInfo().getAppSecret() + token + timestamp + nonce));
        boolean flag = signature.equals(sign);
        Assert.isTrue(flag, "签名错误");
        // 5. 拒绝重复调用(第一次访问时存储，过期时间和请求超时时间保持一致), 只有标注不允许重复提交注解的才会校验
        if (notRepeatSubmit != null) {
            Boolean exists = redisService.exists(sign);
            Assert.isFalse(Boolean.TRUE.equals(exists), "请勿重复提交");
            redisService.setJSON(sign, RConst.Num.STRING_1, expireTime, TimeUnit.MILLISECONDS);
        }
        return true;
    }

    /**
     * 获取方法上的@NotRepeatSubmit注解
     */
    private NotRepeatSubmit getNotRepeatSubmit(Object handler) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            return method.getAnnotation(NotRepeatSubmit.class);
        }
        return null;
    }

}